using UnityEngine;
using System;

namespace AudioStudio
{
    [Serializable]
    ///@brief This type represents the base for all Wwise Types that require a GUID.
    public class AudioBaseType
    {
#if UNITY_EDITOR
        public byte[] valueGuid = new byte[16];
#endif

        public int ID = 0;
        public uint GetID() { return (uint)ID; }

        public string name = string.Empty;
        public string GetName() { return name; }

        public virtual bool IsValid()
        {
            return ID != 0;
        }

        public bool Validate()
        {
            if (IsValid())
                return true;

            AkSoundEngine.LogWarning("Wwise ID has not been resolved. Consider picking a new " + GetType().Name + ".");
            return false;
        }

        protected void Verify(AKRESULT result)
        {
#if UNITY_EDITOR
            if (result != AKRESULT.AK_Success)
                AkSoundEngine.LogWarning("Unsuccessful call made on " + GetType().Name + ".");
#endif
        }
    }

    [Serializable]
    ///@brief This type represents the base for all Wwise Types that also require a group GUID, such as State and Switch.
    public class AudioBaseGroupType : AudioBaseType
    {
#if UNITY_EDITOR
        public byte[] groupGuid = new byte[16];
#endif

        public int groupID = 0;
        public uint GetGroupID() { return (uint)groupID; }

        public string groupName = string.Empty;
        public string GetGroupName() { return groupName; }

        public override bool IsValid() { return base.IsValid() && groupID != 0; }
    }

    [Serializable]
    ///@brief This type can be used to post events to the sound engine.
    public class AudioEvent : AudioBaseType
    {
        private void VerifyPlayingID(uint playingId)
        {
#if UNITY_EDITOR || AUTO_TEST
            if (playingId == AkSoundEngine.AK_INVALID_PLAYING_ID)
                AkSoundEngine.LogError("Could not post event ID \"" + GetID() + "\". Did you make sure to load the appropriate SoundBank?");
#endif
        }

        public uint Post()
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(GetID(), AudioManager.Instance.GlobalSoundObject);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public uint Post(GameObject gameObj)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            GameObject obj = (gameObj == null) ? AudioManager.Instance.GlobalSoundObject : gameObj;
            uint playingId = AkSoundEngine.PostEvent(GetID(), obj);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public void Stop(int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(AudioManager.Instance.GlobalSoundObject, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void Stop(GameObject gameObj, int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(gameObj, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void ExecuteAction(GameObject gameObj, AkActionOnEventType actionOnEventType, int transitionDuration, AkCurveInterpolation curveInterpolation, bool playerCtrl = false)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.ExecuteActionOnEvent(GetID(), actionOnEventType, gameObj, transitionDuration, curveInterpolation);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to post events to the sound engine.
    public class AudioEventPC : AudioBaseType
    {
        public uint ID_PC = 0;

        private void VerifyPlayingID(uint playingId)
        {
#if UNITY_EDITOR
            if (playingId == AkSoundEngine.AK_INVALID_PLAYING_ID)
                AkSoundEngine.LogError("Could not post event ID \"" + GetID() + "\". Did you make sure to load the appropriate SoundBank?");
#endif
        }

        public uint Post(bool playerCtrl = false)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(playerCtrl ? ID_PC : GetID(), AudioManager.Instance.GlobalSoundObject);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public uint Post(GameObject gameObj, bool playerCtrl = false)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(playerCtrl ? ID_PC : GetID(), gameObj);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public void Stop(int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(AudioManager.Instance.GlobalSoundObject, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void Stop(GameObject gameObj, int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(gameObj, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void ExecuteAction(GameObject gameObj, AkActionOnEventType actionOnEventType, int transitionDuration, AkCurveInterpolation curveInterpolation, bool playerCtrl = false)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.ExecuteActionOnEvent(playerCtrl ? ID_PC : GetID(), actionOnEventType, gameObj, transitionDuration, curveInterpolation);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set game parameter values to the sound engine.
    public class RTPC : AudioBaseType
    {
        public void SetValue(float value, GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetRTPCValue(GetID(), value, gameObject);
                Verify(result);
            }
        }

        public void SetValue(float value)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetRTPCValue(GetID(), value);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to post triggers to the sound engine.
    public class Trigger : AudioBaseType
    {
        public void Post(GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.PostTrigger(GetID(), gameObject);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set Wwise states.
    public class State : AudioBaseGroupType
    {
        public void SetValue()
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetState(GetGroupID(), GetID());
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set switch values on gameobjects.
    public class Switch : AudioBaseGroupType
    {
        public void SetValue(GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetSwitch(GetGroupID(), GetID(), gameObject);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type represents an auxiliary send in the master-mixer hierarchy.
    public class AuxBus : AudioBaseType
    {
    }

    [Serializable]
    ///@brief This type can be used to load/unload soundbanks.
    public class Bank : AudioBaseType
    {
        public void Load()
        {
            if (IsValid())
                AkBankManager.LoadBank(name);
        }

        public void LoadAsync(AkCallbackManager.BankCallback callback = null)
        {
            if (IsValid())
                AkBankManager.LoadBankAsync(name, callback);
        }

        public void Unload()
        {
            if (IsValid())
                AkBankManager.UnloadBank(name);
        }

        public override bool IsValid() { return name.Length != 0 || base.IsValid(); }
    }

    public enum TriggerState
    {
        Enable,
        Disable
    }

    [Serializable]
    public class SoundBankParam
    {
        public bool load = true;
        public bool unload = true;
        public Bank bank;
    }

    [Serializable]
    public class StateParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public State state;

        public void TriggerOnEnable()
        {
            if (this.trigger == TriggerState.Enable)
            {
                this.state.SetValue();
            }
        }

        public void TriggerOnDisable()
        {
            if (this.trigger == TriggerState.Disable)
            {
                this.state.SetValue();
            }
        }
    }

    [Serializable]
    public class SwitchParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public Switch audioSwitch;
    }

    [Serializable]
    public class EffectSoundParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public AudioEvent audioEvent;
        public bool stop = false;
    }

    [Serializable]
    public class SoundParam
    {
        public AudioEventPC audioEvent;
        public bool stop = false;
        public float transitionDuration = 0.2f;
    }
}